Skip to content

Ed25519 support in the OpenSSL compatibility layer#10722

Open
ordex wants to merge 1 commit into
wolfSSL:masterfrom
mandelbitdev:ed25519-compat
Open

Ed25519 support in the OpenSSL compatibility layer#10722
ordex wants to merge 1 commit into
wolfSSL:masterfrom
mandelbitdev:ed25519-compat

Conversation

@ordex

@ordex ordex commented Jun 17, 2026

Copy link
Copy Markdown

Description

wolfCrypt has full Ed25519 (keygen, sign/verify, ASN.1 import/export) and the TLS 1.3 stack already authenticates with Ed25519 certificates, but the OpenSSL-compatibility surface was missing the dispatch for several common operations, so an application driving Ed25519 purely through the OpenSSL API (EVP_PKEY_keygen, i2d_PUBKEY, X509_sign of an in-memory self-signed cert, then loading it into an SSL_CTX) could not use it.

This PR adds the required glue code to have full OpenSSL API support for Ed25591 (by mirroring the existing RSA/ECC/X25519 code).

Note: requires --enable-ed25519 --enable-certgen

Testing

The commit comes with the relevant unit test code.
Moreover, this code is used by an experimental TLS-based VPN client that uses Ed25591 keys/certs.

@wolfSSL-Bot

Copy link
Copy Markdown

Can one of the admins verify this patch?

@dgarske

dgarske commented Jun 17, 2026

Copy link
Copy Markdown
Member

Hi @ordex , this is a very nice contribution. I'd like to hear more about your project and use case. In order to accept this we need some more information and a signed contributor agreement. Please email support@wolfssl.com and reference this PR. Thank you so much, David Garske, wolfSSL

@ordex

ordex commented Jun 17, 2026

Copy link
Copy Markdown
Author

Hi @dgarske I reached out to support and I am sorting out the agreement.
Meanwhile I saw that I made a mistake in the unit-test (some workflow failed on it) and I force pushed a change.

Regarding my project: I am working on a slim openvpn-compatible client and it is expected to perform the TLS handshake using Ed25519 keys. While this works flawlessly with OpenSSL, I hit the mentioned limitation with wolfSSL.

The reason for using also wolfSSL, next to OpenSSL, is because I am planning to deploy the client on OpenWrt, which may ship with wolfSSL only.

@dgarske

dgarske commented Jun 17, 2026

Copy link
Copy Markdown
Member

Okay to test. Thank you @ordex for those details and working with us on the contributor agreement. I see your ZD ticket 22003.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR extends wolfSSL’s OpenSSL-compatibility layer to fully support Ed25519 workflows (EVP keygen, PKCS#8/SPKI serialization, X509 self-signing, and SSL_CTX loading), aligning the OpenSSL API surface with existing wolfCrypt/TLS Ed25519 capabilities.

Changes:

  • Add Ed25519 support to EVP_PKEY_keygen() and public-key DER serialization (i2d_PUBKEY / i2d_PublicKey).
  • Improve Ed25519 PKCS#8 decode handling and ensure decoded private keys have a derived public component.
  • Add an API-level unit test exercising an application-like Ed25519 EVP/X509/TLS flow.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
wolfcrypt/src/evp.c Adds Ed25519 handling to EVP_PKEY key generation and caches PKCS#8 DER.
wolfcrypt/src/evp_pk.c Improves Ed25519 PKCS#8 decode behavior and adds SPKI encoding for Ed25519 public keys.
src/x509.c Adds Ed25519 pubkey extraction/import, Ed25519 signing handling with NULL digest, and X509 pubkey setting.
src/pk.c Extends PKCS#8 encoding logic to return cached Ed25519 PKCS#8 bytes.
tests/api.c Adds an end-to-end Ed25519 OpenSSL-compat EVP/X509/SSL_CTX unit test.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread wolfcrypt/src/evp.c Outdated
Comment on lines 3824 to 3826
#if defined(HAVE_ED25519) && defined(HAVE_ED25519_KEY_EXPORT)
ctx->pkey->type != WC_EVP_PKEY_ED25519 &&
#endif
Comment thread wolfcrypt/src/evp.c Outdated
Comment on lines +3939 to +3940
#if defined(HAVE_ED25519) && defined(HAVE_ED25519_KEY_EXPORT)
case WC_EVP_PKEY_ED25519:
Comment thread wolfcrypt/src/evp.c
Comment on lines +3957 to +3961
if (wc_ed25519_make_key(&pkey->rng, ED25519_KEY_SIZE,
pkey->ed25519) == 0) {
/* Cache the PKCS#8 PrivateKeyInfo DER so the EVP/SSL paths
* (use_PrivateKey, EVP_PKEY2PKCS8) can load and serialize the
* key, mirroring the state the decode path produces. */
Comment thread src/x509.c Outdated
Comment on lines +6582 to +6586
#if defined(HAVE_ED25519)
if (key->type == WC_EVP_PKEY_ED25519) {
key->ed25519 = (ed25519_key*)XMALLOC(sizeof(ed25519_key),
x509->heap, DYNAMIC_TYPE_ED25519);
if (key->ed25519 == NULL) {
Comment thread src/x509.c Outdated
Comment on lines +12522 to +12526
#if defined(HAVE_ED25519)
if (x509->pubKeyOID == ED25519k) {
ed25519 = (ed25519_key*)XMALLOC(sizeof(ed25519_key), NULL,
DYNAMIC_TYPE_ED25519);
if (ed25519 == NULL) {
Comment thread tests/api.c
Comment on lines +10085 to +10087
#if defined(OPENSSL_EXTRA) && defined(HAVE_ED25519) && \
defined(HAVE_ED25519_KEY_EXPORT) && defined(HAVE_ED25519_KEY_IMPORT) && \
defined(WOLFSSL_CERT_GEN) && !defined(NO_CERTS)
Comment thread tests/api.c Outdated
tmp = p8der;
ExpectNotNull(decPriv = wolfSSL_d2i_AutoPrivateKey(NULL, &tmp, p8Sz));

XFREE(p8der, HEAP_HINT, DYNAMIC_TYPE_OPENSSL);
Comment thread tests/api.c Outdated
Comment on lines +10175 to +10176
XFREE(spki, HEAP_HINT, DYNAMIC_TYPE_OPENSSL);
XFREE(spki2, HEAP_HINT, DYNAMIC_TYPE_OPENSSL);
Comment thread src/pk.c
Comment on lines +7378 to +7391
else if (pkey->type == WC_EVP_PKEY_ED25519) {
/* The cached DER is already a PKCS#8 PrivateKeyInfo (set when the
* key was generated or decoded), so return it as-is (same as the
* DH special case above). */
if (keySz == NULL)
return BAD_FUNC_ARG;

*keySz = (word32)pkey->pkey_sz;
if (key == NULL)
return LENGTH_ONLY_E;

XMEMCPY(key, pkey->pkey.ptr, (size_t)pkey->pkey_sz);
return pkey->pkey_sz;
}

@dgarske dgarske left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Skoll Code Review

Scan type: reviewOverall recommendation: REQUEST_CHANGES
Findings: 13 total — 13 posted, 0 skipped
12 finding(s) posted as inline comments (see file-level comments below)

Posted findings

  • [High] Ed25519 pubkey import in CertFromX509/wolfssl_x509_make_der not gated on HAVE_ED25519_KEY_IMPORTsrc/x509.c:12522-12550
  • [High] Ed25519 import paths missing HAVE_ED25519_KEY_IMPORT guard (build break)src/x509.c:6582, src/x509.c:12522
  • [High] Ed25519 public-key decode in wolfSSL_X509_get_pubkey not gated on HAVE_ED25519_KEY_IMPORTsrc/x509.c:6582-6610
  • [Medium] pkcs8_encode Ed25519 special case lacks NULL guard on pkey-pkey.ptrsrc/pk.c:7377-7392
  • [Medium] Non-ASCII em-dash in comment violates wolfSSL ASCII-only conventionsrc/pk.c:7380
  • [Medium] Ed25519 keygen path uses KEY_EXPORT guard but calls MAKE_KEY-only wc_ed25519_make_keywolfcrypt/src/evp.c:3939-3958
  • [Medium] wc_ed25519_make_public derive call requires HAVE_ED25519_MAKE_KEY guardwolfcrypt/src/evp_pk.c:286-295
  • [Low] pkcs8_encode Ed25519 branch assumes cached DER is a private PKCS#8 without validatingsrc/pk.c:7377-7392
  • [Low] Non-ASCII em-dash in comment violates ASCII-only conventionsrc/pk.c:7380
  • [Low] Use CTC_ED25519 instead of ED25519k for signature-type return valuesrc/x509.c:12264-12269
  • [Low] Doxygen comment detached from wolfSSL_i2d_PublicKey by inserted helperwolfcrypt/src/evp_pk.c:2402-2457
  • [Low] i2d_PublicKey for Ed25519 emits SubjectPublicKeyInfo rather than the raw keywolfcrypt/src/evp_pk.c:2459-2488
  • [Low] Redundant pubKeySet assignment and ignored make_public failurewolfcrypt/src/evp_pk.c:290-295

Review generated by Skoll

Comment thread src/x509.c Outdated
Comment thread src/x509.c Outdated
Comment thread src/pk.c
Comment thread src/pk.c
Comment thread wolfcrypt/src/evp.c Outdated
Comment thread src/pk.c
Comment thread src/x509.c
Comment thread wolfcrypt/src/evp_pk.c
Comment thread wolfcrypt/src/evp_pk.c
Comment thread wolfcrypt/src/evp_pk.c

@julek-wolfssl julek-wolfssl left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is great work. I would like to see the following tests included too:

  • Raw-key paths, e.g. EVP_PKEY_new_raw_private_key() / EVP_PKEY_new_raw_public_key(), since those may populate pkey->pkey.ptr differently than keygen/decode.
  • Validation that a PKCS#8-decoded Ed25519 private key can re-export the same public key / SPKI, not just that d2i_AutoPrivateKey() returns non-NULL.
  • Negative coverage for public-only Ed25519 keys: PKCS#8 private export should fail cleanly, not emit raw public bytes or cached non-PKCS#8 data.
  • Certificate verification after X509_sign(..., NULL), to prove the generated Ed25519 signature/OID/public-key encoding is actually usable.

Comment thread src/x509.c Outdated
Comment thread src/x509.c Outdated
Comment thread src/x509.c Outdated
Comment thread wolfcrypt/src/evp.c Outdated
Comment thread wolfcrypt/src/evp.c Outdated
@dgarske

dgarske commented Jun 18, 2026

Copy link
Copy Markdown
Member

Hi @ordex , your contributor agreement has been approved and is now on file. We posted some review feedback, so take a look at those. Looking forward to your next push.
Thanks, David Garske, wolfSSL

@ordex

ordex commented Jun 23, 2026

Copy link
Copy Markdown
Author

I will be squashing the various requested changes into the original commits. I think this is fine, right?
Or you rather wanted to review the changes separately before I do the squash&merge?

@dgarske

dgarske commented Jun 23, 2026

Copy link
Copy Markdown
Member

I will be squashing the various requested changes into the original commits. I think this is fine, right? Or you rather wanted to review the changes separately before I do the squash&merge?

Yes that is fine. Thank you

wolfCrypt has full Ed25519 (keygen, sign/verify, ASN.1 import/export) and
the TLS 1.3 stack already authenticates with Ed25519 certificates, but the
OpenSSL-compatibility surface was missing the dispatch for several common
operations, so an application driving Ed25519 purely through the OpenSSL
API (EVP_PKEY_keygen, i2d_PUBKEY, X509_sign of an in-memory self-signed
cert, X509_verify, then loading it into an SSL_CTX) could not use it.  Fill
those gaps, each mirroring the adjacent RSA/ECC/X25519 case:

- EVP_PKEY_keygen (wolfcrypt/src/evp.c): generate an Ed25519 key and cache
  the PKCS#8 PrivateKeyInfo DER, like the EC/RSA cases; if the DER cannot be
  cached the freshly generated key is freed so a failed keygen never leaves
  a partial key behind.
- wolfSSL_i2d_PublicKey / i2d_PUBKEY (wolfcrypt/src/evp_pk.c): encode an
  Ed25519 SubjectPublicKeyInfo.  i2d_PublicKey intentionally emits the full
  SPKI here (rather than the bare key) because i2d_PUBKEY aliases it and the
  PEM_write_PUBKEY / d2i_PUBKEY round-trip relies on it.
- pkcs8_encode (src/pk.c): return the already-PKCS#8 cached DER for Ed25519
  (as the DH case does) after checking it is present, so
  i2d_PKCS8_PRIV_KEY_INFO works and a public-only key (e.g. one obtained
  from X509_get_pubkey, which caches only the raw public key) is rejected
  rather than emitted as a bogus private key.
- d2iTryEd25519Key (wolfcrypt/src/evp_pk.c): derive the public key after
  decoding a PKCS#8 private key (v1 carries only the seed) so the decoded
  EVP_PKEY is complete.
- d2i_AutoPrivateKey (wolfcrypt/src/evp_pk.c): detect Ed25519 by its
  algorithm id and decode the full PKCS#8 (its inner key is an OCTET
  STRING, which the RSA/ECC sequence-counting heuristic cannot classify).
- X509_sign / X509_resign_cert / sigTypeFromPKEY / wolfssl_x509_make_der
  (src/x509.c): sign a certificate with an Ed25519 key (NULL digest),
  resolving the signature type as CTC_ED25519 and building the
  SubjectPublicKey from an ed25519_key.
- X509_verify (src/x509.c): verify a certificate's signature with an
  Ed25519 EVP_PKEY, exporting the raw public key for the check (the cached
  DER is a PKCS#8 private blob, not the public key the verify path wants).
- X509_set_pubkey / X509_get_pubkey (src/x509.c): set and retrieve an
  Ed25519 public key, keeping the X.509 public-key buffer as the raw key
  bytes to match how DecodeCert/StoreKey store it.

Ed25519 object lifetime goes through the existing wolfSSL_ED25519_new /
wolfSSL_ED25519_free helpers.  Their guards (wolfssl/openssl/ed25519.h,
src/pk.c) and the ssl.c include of openssl/ed25519.h are widened to also
cover OPENSSL_EXTRA_X509_SMALL, since the X509 paths above build in that
configuration too.  The individual operations are gated on
HAVE_ED25519_KEY_IMPORT / HAVE_ED25519_KEY_EXPORT / HAVE_ED25519_MAKE_KEY
so a build that disables one of those sub-features still compiles.

tests/api.c gains test_wolfSSL_EVP_PKEY_ED25519_openssl, which exercises
the whole sequence: keygen, i2d_PUBKEY, the raw-key constructors
(EVP_PKEY_new_raw_public_key / EVP_PKEY_new_raw_private_key), the PKCS#8
round-trip (EVP_PKEY2PKCS8, i2d_PKCS8_PKEY, d2i_AutoPrivateKey) including a
check that the decoded key re-exports the same SPKI, a negative check that
PKCS#8 export of a public-only key fails cleanly, a self-signed X509_sign
with a NULL digest followed by X509_verify of the result, an X509_get_pubkey
round-trip, and loading the key+cert into an SSL_CTX.

Requires --enable-ed25519 --enable-certgen (key import/export and key
generation are on by default with --enable-ed25519).

Signed-off-by: Antonio Quartulli <antonio@mandelbit.com>
@ordex ordex requested a review from julek-wolfssl June 23, 2026 22:06

@julek-wolfssl julek-wolfssl left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ordex Please address the one unresolved issue remaining from David. I commented what is still needed.

@ordex

ordex commented Jun 24, 2026

Copy link
Copy Markdown
Author

@ordex Please address the one unresolved issue remaining from David. I commented what is still needed.

Thanks for pointing that out! Will look at it ASAP.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants